/*
 * KKGestureListener.h
 *
 *  Created on: 2013-9-15
 *      Author: wanghua
 */

#ifndef KKGESTURELISTENER_H_
#define KKGESTURELISTENER_H_

#include "cocos2d.h"
#include "cocos-ext.h"
#include "KKTime.h"
#include "KKTouch.h"
#include <iostream>
#include "KKMathUtils.h"
using namespace std;

USING_NS_CC;
USING_NS_CC_EXT;

class KKGestureListener {
public:

	virtual void onDown(const KKTouch& touch) {
	}
	;

	;
	virtual void onLongDown(const KKTouch& touch) {
	}
	;

	virtual void onSingleTap(const KKTouch& touch) {
	}
	;

	virtual void onTap(const KKTouch& touch) {
	}
	;

	virtual void onDoubleTab(const KKTouch& touch) {
	}
	;

	virtual void onLongTap(const KKTouch& touch) {
	}
	;

	virtual void onScroll(const KKTouch& touch, const CCPoint& dir) {
	}
	;

	virtual void onFling(const KKTouch& touch, const CCPoint& velocity) {
	}
	;

	virtual void onUp(const KKTouch& touch) {
	}
	;

	virtual void onZoom(const CCPoint& zoom) {

	}
	;

	virtual void onRotate(const float& angle) {

	}
	;

	virtual void onDrag(const CCPoint& drag) {

	}
	;
};

#ifndef HISTORY_SIZE
#define HISTORY_SIZE 5
#endif

class KKTouchInfo: public CCObject {
public:
	int viewId;
	CCPoint points[HISTORY_SIZE];
	long times[HISTORY_SIZE];
	int index;

	KKTouchInfo() :
			index(-1) {

	}
	;

	void addPoint(long time, CCPoint point) {
		if (++index == HISTORY_SIZE) {
			index = 0;
		}

		times[index] = time;
		points[index] = point;
	}
};

typedef KKDictionary<int, KKTouchInfo*> KKTouchInfoDictionary;

class KKVelocityTracker: public cocos2d::CCObject {
private:

	KKTouchInfoDictionary* m_touchInfos;

public:

	KKVelocityTracker() {
		m_touchInfos = NULL;
	}
	;
	virtual ~KKVelocityTracker() {
		CC_SAFE_DELETE(m_touchInfos);
	}
	;

	inline KKTouchInfo* getTouchInfo(const KKTouch& touch) {
		return m_touchInfos->objectForKey(touch.getViewId());
	}

	void trackStart(const KKTouch& touch) {

		if (m_touchInfos == NULL) {
			m_touchInfos = new KKTouchInfoDictionary();
		}

		KKTouchInfo* touchInfo = m_touchInfos->objectForKey(touch.getViewId());

		if (!touchInfo) {
			touchInfo = new KKTouchInfo();
			touchInfo->viewId = touch.getViewId();
			m_touchInfos->setObject(touchInfo, touch.getViewId());
		}

		touchInfo->addPoint(KKTime::getStartupTime(), touch.getPoint());
	}

	void trackPoint(const KKTouch& touch) {

		KKTouchInfo* touchInfo = m_touchInfos->objectForKey(touch.getViewId());

		if (!touchInfo) {
			return;
		}

		touchInfo->addPoint(KKTime::getStartupTime(), touch.getPoint());

	}

	void trackEnd(const KKTouch& touch) {

		KKTouchInfo* touchInfo = m_touchInfos->objectForKey(touch.getViewId());

		if (!touchInfo) {
			return;
		}

		touchInfo->addPoint(KKTime::getStartupTime(), touch.getPoint());
	}

	void trackDestroy(const KKTouch& touch) {
		m_touchInfos->removeObjectForKey(touch.getViewId());
	}

	CCPoint getVelocity(const KKTouch& touch) {

		KKTouchInfo* touchInfo = m_touchInfos->objectForKey(touch.getViewId());

		if (!touchInfo) {
			return CCPointZero;
		}

		float x[HISTORY_SIZE];
		float y[HISTORY_SIZE];
		float time[HISTORY_SIZE];
		int m = 0;
		int index = touchInfo->index;

		long& newestTime = touchInfo->times[index];
		do {

			long& touchTime = touchInfo->times[index];

			//CCLog("touchTime t=%d\n",touchTime);

			long age = newestTime - touchTime;

			if (age > 1000) {
				break;
			}

			x[m] = touchInfo->points[index].x;
			y[m] = touchInfo->points[index].y;

			time[m] = -age / 1000.0;

			index = (index == 0 ? HISTORY_SIZE : index) - 1;
		} while (++m < HISTORY_SIZE);

		if (m < 1) {
			return CCPointZero; // no data
		}

		float xdet, ydet;
		int n = 2;

		float xcoeff[HISTORY_SIZE];
		float ycoeff[HISTORY_SIZE];

		//	CCLog("solveLeastSquares n=%d,m=%d\n",n,m);

		if (solveLeastSquares(time, x, m, n, xcoeff, &xdet)
				&& solveLeastSquares(time, y, m, n, ycoeff, &ydet)) {
			return CCPoint(xcoeff[1], ycoeff[1]);
		}

		return CCPointZero;
	}
	;

};

class KKNode;

class KKGestureDirector: public cocos2d::CCObject {
private:
	KKTouchHolder* m_touchHolder;
	KKVelocityTracker velocityTracker;
	bool m_singleTap;
	bool m_longTap;

	;CC_SYNTHESIZE(KKTouch*,m_firstTouch,FirstTouch)
	;CC_SYNTHESIZE(KKTouch*,m_secondTouch,SecondTouch)
	;

	;CC_SYNTHESIZE(bool,m_flingEnabled,FlingEnabled)
	;CC_SYNTHESIZE(KKGestureListener*,m_GestureListener,GestureListener)
	;CC_SYNTHESIZE(KKNode*,m_target,Target)
	;
public:
	KKGestureDirector();
	virtual ~KKGestureDirector();

	virtual bool onTouchDown(KKTouch& touch);

	virtual void onTouchMove(KKTouch& touch);

	virtual void onTouchUp(KKTouch& touch);

	void onGensture(KKTouch& touch);

	void onCheckSingleTap(ccTime time);

	void onCheckLongTap(ccTime time);

	static KKGestureDirector* create(KKNode* target);
};
#endif /* KKGESTURELISTENER_H_ */
